#define __STDC_LIMIT_MACROS 1
#define __STDC_CONSTANT_MACROS 1


#include <stdio.h>

#include "llvm/DerivedTypes.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/PassManager.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetSelect.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdio>
#include <string>
#include <map>
#include <vector>

#include "CompileLlvm.h"

using namespace llvm;

static IRBuilder<> Builder(getGlobalContext());

#if 0
static std::map<std::string, llvm::Value*> namedValues;
typedef std::vector<std::string> ArgNames;
typedef std::vector<const llvm::Type*> ArgTypes;
Function *createFunction(Module& module,
                               const llvm::Type* retType,
                               const ArgTypes& theArgTypes,
                               const ArgNames& theArgNames,
                               const std::string& functName,
                               llvm::GlobalValue::LinkageTypes linkage,
                               bool declarationOnly,
                               bool isVarArg)
{
  llvm::FunctionType* functType = llvm::FunctionType::get(retType, 
      theArgTypes, 
      isVarArg);
  llvm::Function* ret = llvm::Function::Create(functType, 
      linkage, 
      functName, 
      &module);
  if (!ret || declarationOnly)
    return(ret);

  namedValues.clear();
  unsigned i = 0; 
  for (llvm::Function::arg_iterator argIndex = ret->arg_begin();
      i != theArgNames.size();
      ++argIndex, ++i) {

    argIndex->setName(theArgNames[i]);
    namedValues[theArgNames[i]] = argIndex;
  }

  return(ret);
}
#endif

CompileLlvm::CompileLlvm(const char * ofn)
  : Context( getGlobalContext() )
{
  InitializeNativeTarget();

  module = new Module("ofn", Context);

  // create default 'main' function
  std::vector<const Type*> Doubles(2, Type::getDoubleTy(getGlobalContext()));
  FunctionType *FT = FunctionType::get(Type::getDoubleTy(getGlobalContext()),
      Doubles, false);
  function = Function::Create(FT, Function::ExternalLinkage, "main", module);
  BasicBlock::Create(Context, "EntryBlock", function);
  
  // create default 'printLiteral' function
  std::vector<const Type*> printLiteralArgs(1, Type::getInt8Ty(getGlobalContext())->getPointerTo());
  FunctionType *printLiteralType = FunctionType::get(Type::getDoubleTy(getGlobalContext()),
      printLiteralArgs, false);
  function = Function::Create(printLiteralType, Function::ExternalLinkage, "printStr", module);
  BasicBlock::Create(Context, "EntryBlock", function);
}

int CompileLlvm::finalize()
{
  // Output the bitcode file to stdout
  //WriteBitcodeToFile(module, outs());
  assert(blockStack.size()==0);
  module->dump();
  return 0;
}

int CompileLlvm::compile(AstNodeDoubleConstant * node)
{ 
  Value *v = ConstantInt::get(Type::getDoubleTy(Context), node->getValue());
  blockStack.push(v);
  return 0;
}

int CompileLlvm::compile(AstNodeLiteral * node)
{ 
  llvm::Constant* s = 
        llvm::ConstantArray::get(Context, node->getString());
  blockStack.push(s);
  return 0; 
}

int CompileLlvm::compile(AstNodeStatementList * node)
{ 
  std::vector<AstNode*> nodes=node->getChildren();
  unsigned i;
  for(i=0;i<nodes.size();i++) {
    nodes[i]->compile(this);
  }
  return 0; 
}

int CompileLlvm::compile(AstNodeExpressionVariable * node)
{ 
  std::string v(node->getName());
  Value *V = NamedValues[v];
  assert(V);
  blockStack.push(V);
  return 0;
}

int CompileLlvm::compile(AstNodeBinaryOperator * node)
{
  int oper=node->getOperator();
  AstNodeExpression * left=node->getLeft();
  AstNodeExpression * rite=node->getRite();

  left->compile(this);
  rite->compile(this);

  Value * L=blockStack.top();
  Value * R=blockStack.top();
  Value * retV=0;

  blockStack.pop();
  blockStack.pop();

  assert (L && R);

  switch (oper) {
    case '+': retV=Builder.CreateFAdd(L, R, "addtmp");
    case '-': retV=Builder.CreateFSub(L, R, "subtmp");
    case '*': retV=Builder.CreateFMul(L, R, "multmp");
    case '/': retV=Builder.CreateFMul(L, R, "divtmp");
    default: assert(0);
  }
  blockStack.push(retV);
}

int CompileLlvm::compile(AstNodePrintExpression * node)
{ 
  AstNodeExpression * expr=node->getExpression();
  expr->compile(this);

  Value * v=blockStack.top();
  blockStack.pop();

  Function *CalleeF = module->getFunction("printStr");
  assert(CalleeF);

  std::vector<Value*> ArgsV;
  ArgsV.push_back(v);

  Value * call=Builder.CreateCall(CalleeF, ArgsV.begin(), ArgsV.end(), "calltmp");
  blockStack.push(call);
  printf("------\n");
  return 0;
}


#define place_holder(x) { printf("<-> %s \n",__PRETTY_FUNCTION__); }

int CompileLlvm::compile(AstNodePrintLiteral *) { place_holder(0); return 0;  }
int CompileLlvm::compile(AstNodeLValue*){ place_holder(0 ); return 0;  }
int CompileLlvm::compile(AstNodeBooleanOperator *){ place_holder(0 ); return 0;  }
int CompileLlvm::compile(AstNodeAssignment *){ place_holder(0 ); return 0;  }
int CompileLlvm::compile(AstNodeCompareExpression *){ place_holder(0 ); return 0;  }
int CompileLlvm::compile(AstNodeForStatement *){ place_holder(0 ); return 0;  }
int CompileLlvm::compile(AstNodeIfStatement *){ place_holder(0 ); return 0;  }

